<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title>The source code</title>
  <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
  <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
  <style type="text/css">
    .highlight { display: block; background-color: #ddd; }
  </style>
  <script type="text/javascript">
    function highlight() {
      document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
    }
  </script>
</head>
<body onload="prettyPrint(); highlight();">
  <pre class="prettyprint lang-js">var Eventful = require(&quot;./event/Eventful&quot;);

var classUtil = require(&quot;./core/utils/class_util&quot;);

var util = require(&quot;./core/utils/data_structure_util&quot;);

var env = require(&quot;./core/env&quot;);

var Group = require(&quot;./graphic/Group&quot;);

var timsort = require(&quot;./core/utils/timsort&quot;);

// Use timsort because in most case elements are partially sorted
// https://jsfiddle.net/pissang/jr4x7mdm/8/

<span id='qrenderer-core-Storage'>/**
</span> * @class qrenderer.core.Storage
 * 内容仓库 (M)，用来存储和管理画布上的所有对象，同时提供绘制和更新队列的功能。
 * 需要绘制的对象首先存储在 Storage 中，然后 Painter 类会从 Storage 中依次取出进行绘图。
 * 利用 Storage 作为内存中转站，对于不需要刷新的对象可以不进行绘制，从而可以提升整体性能。
 * @docauthor 大漠穷秋 damoqiongqiu@126.com
 */

<span id='qrenderer-core-Storage-method-constructor'>/**
</span> * @method constructor Storage
 */
var Storage = function Storage() {
  // jshint ignore:line

<span id='qrenderer-core-Storage-property-_roots'>  /**
</span>   * @private
   * @property _roots
   */
  this._roots = new Map(); //直接放在画布上的对象为根对象

<span id='qrenderer-core-Storage-property-_displayList'>  /**
</span>   * @private
   * @property _displayList
   */

  this._displayList = [];
<span id='qrenderer-core-Storage-property-_displayListLen'>  /**
</span>   * @private
   * @property _displayListLen
   */

  this._displayListLen = 0;
  classUtil.inheritProperties(this, Eventful);
};

Storage.prototype = {
  constructor: Storage,

<span id='qrenderer-core-Storage-method-traverse'>  /**
</span>   * @method traverse
   * @param  {Function} cb
   * @param  {Object} context
   */
  traverse: function traverse(cb, context) {
    this._roots.forEach(function (el, id, map) {
      el.traverse(cb, context);
    });
  },

<span id='qrenderer-core-Storage-method-getDisplayList'>  /**
</span>   * @method getDisplayList
   * 返回所有图形的绘制队列
   * @param {boolean} [needUpdate=false] 是否在返回前更新该数组
   * @param {boolean} [includeIgnore=false] 是否包含 ignore 的数组, 在 needUpdate 为 true 的时候有效
   *
   * 详见{@link Displayable.prototype.updateDisplayList}
   * @return {Array&lt;Displayable&gt;}
   */
  getDisplayList: function getDisplayList(needUpdate, includeIgnore) {
    includeIgnore = includeIgnore || false;

    if (needUpdate) {
      this.updateDisplayList(includeIgnore);
    }

    return this._displayList;
  },

<span id='qrenderer-core-Storage-method-updateDisplayList'>  /**
</span>   * @method updateDisplayList
   * 更新图形的绘制队列。
   * 每次绘制前都会调用，该方法会先深度优先遍历整个树，更新所有Group和Shape的变换并且把所有可见的Shape保存到数组中，
   * 最后根据绘制的优先级（zlevel &gt; z &gt; 插入顺序）排序得到绘制队列
   * @param {boolean} [includeIgnore=false] 是否包含 ignore 的数组
   */
  updateDisplayList: function updateDisplayList(includeIgnore) {
    var _this = this;

    this._displayListLen = 0;
    var displayList = this._displayList;

    this._roots.forEach(function (el, id, map) {
      _this._updateAndAddDisplayable(el, null, includeIgnore); //recursive update

    });

    displayList.length = this._displayListLen;
    env.canvasSupported &amp;&amp; timsort(displayList, this.displayableSortFunc);
  },

<span id='qrenderer-core-Storage-method-_updateAndAddDisplayable'>  /**
</span>   * @method _updateAndAddDisplayable
   * @param {*} el 
   * @param {*} clipPaths 
   * @param {*} includeIgnore 
   */
  _updateAndAddDisplayable: function _updateAndAddDisplayable(el, clipPaths, includeIgnore) {
    if (el.ignore &amp;&amp; !includeIgnore) {
      return;
    }

    if (el.__dirty) {
      el.composeLocalTransform();
    }

    var userSetClipPath = el.clipPath;

    if (userSetClipPath) {
      // FIXME 效率影响
      if (clipPaths) {
        clipPaths = clipPaths.slice();
      } else {
        clipPaths = [];
      }

      var currentClipPath = userSetClipPath;
      var parentClipPath = el; // Recursively add clip path

      while (currentClipPath) {
        // clipPath 的变换是基于使用这个 clipPath 的元素
        currentClipPath.parent = parentClipPath;
        currentClipPath.composeLocalTransform();
        clipPaths.push(currentClipPath);
        parentClipPath = currentClipPath;
        currentClipPath = currentClipPath.clipPath;
      }
    }

    if (el.type === &#39;group&#39;) {
      var children = el.children;

      for (var i = 0; i &lt; children.length; i++) {
        var child = children[i]; // Force to mark as dirty if group is dirty
        // FIXME __dirtyPath ?

        if (el.__dirty) {
          child.__dirty = true;
        }

        this._updateAndAddDisplayable(child, clipPaths, includeIgnore);
      } // Mark group clean here


      el.__dirty = false;
    } else {
      el.__clipPaths = clipPaths;
      this._displayList[this._displayListLen++] = el;
    }
  },

<span id='qrenderer-core-Storage-method-addToRoot'>  /**
</span>   * @method addToRoot
   * 添加图形(Shape)或者组(Group)到根节点
   * @param {Element} el
   */
  addToRoot: function addToRoot(el) {
    if (el.__storage === this) {
      return;
    }

    this.trigger(&quot;beforeAddToRoot&quot;, el);
    el.trigger(&quot;beforeAddToRoot&quot;, el);
    this.addToStorage(el);
  },

<span id='qrenderer-core-Storage-method-delFromRoot'>  /**
</span>   * @method
   * 删除指定的图形(Shape)或者组(Group)
   * @param {string|Array.&lt;String&gt;} [el] 如果为空清空整个Storage
   */
  delFromRoot: function delFromRoot(el) {
    var _this2 = this;

    if (el == null) {
      // Clear all.
      this._roots.forEach(function (el, id, map) {
        _this2.delFromStorage(el);
      });

      this._roots = new Map();
      this._displayList = [];
      this._displayListLen = 0;
      return;
    }

    if (el.forEach) {
      // Array like.
      el.forEach(function (item, index) {
        _this2.delFromRoot(item);
      });
      return;
    }

    this.delFromStorage(el);
  },

<span id='qrenderer-core-Storage-method-addToStorage'>  /**
</span>   * @method addToStorage
   * @param {Element} el 
   */
  addToStorage: function addToStorage(el) {
    this._roots.set(el.id, el);

    this.trigger(&quot;addToStorage&quot;, el);
    el.trigger(&quot;addToStorage&quot;, this);
    return this;
  },

<span id='qrenderer-core-Storage-method-delFromStorage'>  /**
</span>   * @method delFromStorage
   * @param {Element} el 
   */
  delFromStorage: function delFromStorage(el) {
    if (this._roots.get(el.id)) {
      this._roots[&quot;delete&quot;](el.id);

      this.trigger(&quot;delFromStorage&quot;, el);
      el.trigger(&quot;delFromStorage&quot;, this);
    }

    return this;
  },

<span id='qrenderer-core-Storage-method-dispose'>  /**
</span>   * @method dispose
   * 清空并且释放Storage
   */
  dispose: function dispose() {
    this._renderList = null;
    this._roots = null;
  },
  displayableSortFunc: function displayableSortFunc(a, b) {
    if (a.qlevel === b.qlevel) {
      if (a.z === b.z) {
        // if (a.z2 === b.z2) {
        //     // FIXME Slow has renderidx compare
        //     // http://stackoverflow.com/questions/20883421/sorting-in-javascript-should-every-compare-function-have-a-return-0-statement
        //     // https://github.com/v8/v8/blob/47cce544a31ed5577ffe2963f67acb4144ee0232/src/js/array.js#L1012
        //     return a.__renderidx - b.__renderidx;
        // }
        return a.z2 - b.z2;
      }

      return a.z - b.z;
    }

    return a.qlevel - b.qlevel;
  }
};
classUtil.mixin(Storage, Eventful);
var _default = Storage;
module.exports = _default;</pre>
</body>
</html>
